译者:陈广 日期:2020-6-21
在上一篇文章中,我们已经配置好开发环境以及外围代码,本文讲解 ST25DV64K 的读写操作。首先我们要了解 ST25DV64K 存储器的结构。
ST25DV64K 的存储器被划分为如下图所示的四个部分:
我这里只是领大家入个门,更详细的信息请参考《ST25DV64K 中文手册》。
读写操作比较简单,因为 STM 已经帮我们包装好了。在【custom_nfc04a1_nfctag.c】文件中,找两个函数:CUSTOM_NFCTAG_ReadData
和CUSTOM_NFCTAG_WriteData
。
读操作原型为:
int32_t CUSTOM_NFCTAG_ReadData( uint32_t Instance, uint8_t * const pData, const uint16_t TarAddr, const uint16_t Size )
其中,pData
是用于存放读出来的数据;TarAddr
表示读取的起始地址;Size
表示读多少个字节的数据。
写操作原型为:
int32_t CUSTOM_NFCTAG_WriteData( uint32_t Instance, const uint8_t * const pData, const uint16_t TarAddr, const uint16_t Size )
其中,pData
为写缓冲,我们将要写入的数据存放在这里;TarAddr
表示写入的起始地址;Size
表示写多少个字节的数据。
下面我们编写一个程序,每次向 ST25DV64K 的 EEDPROM 写入 10 个字节的数据,然后将这 10 个数据读出来。反复读写,从 0~255。将上篇文章中main()
函数参照以下代码进行更改:
/* USER CODE BEGIN 2 */
//如果ST25DV64K初始化成功,则点亮 LED
while( CUSTOM_NFCTAG_Init(CUSTOM_NFCTAG_INSTANCE) != NFCTAG_OK );
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
uint8_t writeData[10];
uint8_t readData[10];
int index=0;
HAL_Delay(5000);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (index<256)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
//写入数据
int i;
for(i=0;i<10;i++) //将10字节数据压入缓冲
{
writeData[i]=i+index;
}
//写入 NFC 标签
if(CUSTOM_NFCTAG_WriteData(CUSTOM_NFCTAG_INSTANCE, writeData, index, 10)==NFCTAG_OK)
{
printf("Write Data Success! Start = %d\r\n", index);
}
else
{
printf("Write Data Error! Start = %d\r\n", index);
}
//读取数据
if(CUSTOM_NFCTAG_ReadData(CUSTOM_NFCTAG_INSTANCE, readData, index, 10)==NFCTAG_OK)
{
int i;
printf("ADDR%d = ",index);
for(i=0;i<10;i++)
{
printf("%02X ",readData[i]);
}
}
else
{
printf("Read Error! Start = %d\r\n", index);
}
printf("\r\n");
index+=10;
HAL_Delay(2000);
}
/* USER CODE END 3 */
运行结果如下图所示:
掌握基本的读写操作后,我们来对存储器进行分区。新买回来的芯片只有一个分区,我们现在将其划分为相等的四个部分,并将每个分区的读写权限进行不同的设置,以方便后面的实验。
参照以下代码对main()
函数进行更改:
/* USER CODE BEGIN 2 */
//如果ST25DV64K初始化成功,则点亮 LED
while( CUSTOM_NFCTAG_Init(CUSTOM_NFCTAG_INSTANCE) != NFCTAG_OK );
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
HAL_Delay(5000); //延时 5 秒,以留给用户连接上位机的时间
//在更改存储器结构前需要进行一些设置
CUSTOM_NFCTAG_ResetMBEN_Dyn(CUSTOM_NFCTAG_INSTANCE);
ST25DV_I2CSSO_STATUS i2csso;
CUSTOM_NFCTAG_ReadI2CSecuritySession_Dyn(CUSTOM_NFCTAG_INSTANCE,&i2csso);
if(i2csso == ST25DV_SESSION_CLOSED)
{ //划分及设置存储器前需要进行密码验证
ST25DV_PASSWD pwd;
pwd.MsbPasswd = 0;
pwd.LsbPasswd = 0;
CUSTOM_NFCTAG_PresentI2CPassword(CUSTOM_NFCTAG_INSTANCE,pwd);
printf("authentication done!\r\n");
}
/*将存储器划分四个相等区域,每个区域大小为2048字节
_______________
@0000| |
| |
| Zone 1 |
| |
@2048|---------------|
| |
| Zone 2 |
| |
@4096|---------------|
| |
| Zone 3 |
| |
@6144|---------------|
| |
| Zone 4 |
| |
@8192|_______________|
*/
//将存储器划分为四个大小相等的区
CUSTOM_NFCTAG_CreateUserZone(CUSTOM_NFCTAG_INSTANCE,2048,2048,2048,2048);
printf("Create User Zone done\r\n");
HAL_Delay(2000);
//将四个区域分别设置为不同的权限
int32_t ret;
//将第一个区域设置为无保护
ret = CUSTOM_NFCTAG_WriteI2CProtectZonex(CUSTOM_NFCTAG_INSTANCE,ST25DV_PROT_ZONE1,ST25DV_NO_PROT);
if(ret == NFCTAG_OK)
{
printf("Zone 1 set no protect success!\r\n");
}
else
{
printf("Zone 1 set no protect failed!\r\n");
}
HAL_Delay(2000);
//将第二个区域设置为写保护(即写入需要密码)
ret = CUSTOM_NFCTAG_WriteI2CProtectZonex(CUSTOM_NFCTAG_INSTANCE,ST25DV_PROT_ZONE2,ST25DV_WRITE_PROT);
if(ret == NFCTAG_OK)
{
printf("Zone 2 set write protect success!\r\n");
}
else
{
printf("Zone 2 set write protect failed!\r\n");
}
HAL_Delay(2000);
//将第三个区域设置为读保护(即读取需要密码)
ret = CUSTOM_NFCTAG_WriteI2CProtectZonex(CUSTOM_NFCTAG_INSTANCE,ST25DV_PROT_ZONE3,ST25DV_READ_PROT);
if(ret == NFCTAG_OK)
{
printf("Zone 3 set read protect success!\r\n");
}
else
{
printf("Zone 3 set read protect failed!\r\n");
}
HAL_Delay(2000);
//将第四个区域设置为读写保护(读写都需要密码)
ret = CUSTOM_NFCTAG_WriteI2CProtectZonex(CUSTOM_NFCTAG_INSTANCE,ST25DV_PROT_ZONE4,ST25DV_READWRITE_PROT);
if(ret == NFCTAG_OK)
{
printf("Zone 4 set read/write protect success!\r\n");
}
else
{
printf("Zone 4 set read/write protect failed!\r\n");
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
运行结果:
authentication done!
Create User Zone done
Zone 1 set no protect success!
Zone 2 set write protect success!
Zone 3 set read protect success!
Zone 4 set read/write protect success!
需要注意的是,芯片的出厂密码是全0。另外,比较诡异的是,经过实验,我发现CUSTOM_NFCTAG_PresentI2CPassword
和CUSTOM_NFCTAG_CreateUserZone
方法即使失败,其返回值还是NFCTAG_OK
,没办法通过返回值来判定这两个方法是否成功执行,所以在程序中我并未判断其返回值。
下面以不同的验证方式进行读写操作,以观察对四个区的操作有何不同。
首先不进行密码验证而分别对四个区进行读写操作,将main()
函数代码更改如下:
/* USER CODE BEGIN 2 */
//如果ST25DV64K初始化成功,则点亮 LED
while( CUSTOM_NFCTAG_Init(CUSTOM_NFCTAG_INSTANCE) != NFCTAG_OK );
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
HAL_Delay(5000); //延时 5 秒,以留给用户连接上位机的时间
/*将存储器划分四个相等区域,每个区域大小为2048字节
_______________
@0000| |
| |
| Zone 1 |
| no protect |
@2048|---------------|
| |
| Zone 2 |
| write protect |
@4096|---------------|
| |
| Zone 3 |
| read protect |
@6144|---------------|
| |
| Zone 4 |
| read write protect
@8192|_______________|
*/
/* USER CODE END 2 */
uint8_t writeData[10]={1,2,3,4,5,6,7,8,9,10};
uint8_t readData[10];
//读写第一区数据
//写入 NFC 标签
if(CUSTOM_NFCTAG_WriteData(CUSTOM_NFCTAG_INSTANCE, writeData, 0, 10)==NFCTAG_OK)
{
printf("Write Data Success!\r\n");
}
else
{
printf("Write Data Error!\r\n");
}
HAL_Delay(1000);
//读取数据
if(CUSTOM_NFCTAG_ReadData(CUSTOM_NFCTAG_INSTANCE, readData, 0, 10)==NFCTAG_OK)
{
int i;
printf("ADDR 0 = ");
for(i=0;i<10;i++)
{
printf("%02X ",readData[i]);
}
printf("\r\n");
}
else
{
printf("ADDR 0 read error!\r\n");
}
HAL_Delay(1000);
//读写第二区数据
//写入 NFC 标签
if(CUSTOM_NFCTAG_WriteData(CUSTOM_NFCTAG_INSTANCE, writeData, 2048, 10)==NFCTAG_OK)
{
printf("Write Data Success!\r\n");
}
else
{
printf("Write Data Error!\r\n");
}
HAL_Delay(1000);
//读取数据
if(CUSTOM_NFCTAG_ReadData(CUSTOM_NFCTAG_INSTANCE, readData, 2048, 10)==NFCTAG_OK)
{
int i;
printf("ADDR 2048 = ");
for(i=0;i<10;i++)
{
printf("%02X ",readData[i]);
}
printf("\r\n");
}
else
{
printf("ADDR 2048 read error!\r\n");
}
HAL_Delay(1000);
//读写第三区数据
//写入 NFC 标签
if(CUSTOM_NFCTAG_WriteData(CUSTOM_NFCTAG_INSTANCE, writeData, 4096, 10)==NFCTAG_OK)
{
printf("Write Data Success!\r\n");
}
else
{
printf("Write Data Error!\r\n");
}
HAL_Delay(1000);
//读取数据
if(CUSTOM_NFCTAG_ReadData(CUSTOM_NFCTAG_INSTANCE, readData, 4096, 10)==NFCTAG_OK)
{
int i;
printf("ADDR 4096 = ");
for(i=0;i<10;i++)
{
printf("%02X ",readData[i]);
}
printf("\r\n");
}
else
{
printf("ADDR 4096 read error!\r\n");
}
HAL_Delay(1000);
//读写第四区数据
//写入 NFC 标签
if(CUSTOM_NFCTAG_WriteData(CUSTOM_NFCTAG_INSTANCE, writeData, 6144, 10)==NFCTAG_OK)
{
printf("Write Data Success!\r\n");
}
else
{
printf("Write Data Error!\r\n");
}
HAL_Delay(1000);
//读取数据
if(CUSTOM_NFCTAG_ReadData(CUSTOM_NFCTAG_INSTANCE, readData, 6144, 10)==NFCTAG_OK)
{
int i;
printf("ADDR 6144 = ");
for(i=0;i<10;i++)
{
printf("%02X ",readData[i]);
}
printf("\r\n");
}
else
{
printf("ADDR 6144 read error!");
}
HAL_Delay(1000);
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
运行结果:
Write Data Success!
ADDR 0 = 01 02 03 04 05 06 07 08 09 0A
Write Data Error!
ADDR 2048 = 00 00 00 00 00 00 00 00 00 00
Write Data Success!
ADDR 4096 = FF FF FF FF FF FF FF FF FF FF
Write Data Error!
ADDR 6144 = FF FF FF FF FF FF FF FF FF FF
可以看到,读错误也返回NFCTAG_OK
,但返回的全是0xFF
。此次读写结果完全符合每个区所对应的保护状态。
不改变之前的代码,在
//如果ST25DV64K初始化成功,则点亮 LED
while( CUSTOM_NFCTAG_Init(CUSTOM_NFCTAG_INSTANCE) != NFCTAG_OK );
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
HAL_Delay(5000); //延时 5 秒,以留给用户连接上位机的时间
后方添加如下代码:
CUSTOM_NFCTAG_ResetMBEN_Dyn(CUSTOM_NFCTAG_INSTANCE);
ST25DV_I2CSSO_STATUS i2csso;
CUSTOM_NFCTAG_ReadI2CSecuritySession_Dyn(CUSTOM_NFCTAG_INSTANCE,&i2csso);
if(i2csso == ST25DV_SESSION_CLOSED)
{ //划分及设置存储器前需要进行密码验证
ST25DV_PASSWD pwd;
pwd.MsbPasswd = 1;
pwd.LsbPasswd = 2;
CUSTOM_NFCTAG_PresentI2CPassword(CUSTOM_NFCTAG_INSTANCE,pwd);
printf("authentication done!\r\n");
}
运行结果:
authentication done!
Write Data Success!
ADDR 0 = 01 02 03 04 05 06 07 08 09 0A
Write Data Error!
ADDR 2048 = 00 00 00 00 00 00 00 00 00 00
Write Data Success!
ADDR 4096 = FF FF FF FF FF FF FF FF FF FF
Write Data Error!
ADDR 6144 = FF FF FF FF FF FF FF FF FF FF
可以看到结果跟不进行密码验证完全一样,逻辑上说也应该一样,这个实验白做。
将刚才的代码更改如下:
CUSTOM_NFCTAG_ResetMBEN_Dyn(CUSTOM_NFCTAG_INSTANCE);
ST25DV_I2CSSO_STATUS i2csso;
CUSTOM_NFCTAG_ReadI2CSecuritySession_Dyn(CUSTOM_NFCTAG_INSTANCE,&i2csso);
if(i2csso == ST25DV_SESSION_CLOSED)
{ //划分及设置存储器前需要进行密码验证
ST25DV_PASSWD pwd;
pwd.MsbPasswd = 0;
pwd.LsbPasswd = 0;
CUSTOM_NFCTAG_PresentI2CPassword(CUSTOM_NFCTAG_INSTANCE,pwd);
printf("authentication done!\r\n");
}
其实就是将密码改为 0,运行结果如下:
authentication done!
Write Data Success!
ADDR 0 = 01 02 03 04 05 06 07 08 09 0A
Write Data Success!
ADDR 2048 = 01 02 03 04 05 06 07 08 09 0A
Write Data Success!
ADDR 4096 = 01 02 03 04 05 06 07 08 09 0A
Write Data Success!
ADDR 6144 = 01 02 03 04 05 06 07 08 09 0A
密码正确后,所有操作均成功运行。
ST25DV64K的基本操作也就这些了,下面要转战 Android 了,之前弄的安卓现在也差不多忘完了,又得重来一次,太痛苦了!
;